
/*
 XMail OSAX -- Copyright (C) 2004, 2005, 2006, 2007 Jean-Baptiste LE STANG osax@lestang.org
 
	
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */


#include "mail-algorithms.h"

enum BASE64
{
	B64_A, B64_B, B64_C, B64_D, B64_E, B64_F, B64_G, B64_H, B64_I, B64_J, B64_K, B64_L, B64_M, B64_N, B64_O, B64_P, B64_Q, B64_R, B64_S, B64_T, B64_U, B64_V, B64_W, B64_X, B64_Y, B64_Z,
	B64_a, B64_b, B64_c, B64_d, B64_e, B64_f, B64_g, B64_h, B64_i, B64_j, B64_k, B64_l, B64_m, B64_n, B64_o, B64_p, B64_q, B64_r, B64_s, B64_t, B64_u, B64_v, B64_w, B64_x, B64_y, B64_z,
	B64_0, B64_1, B64_2, B64_3, B64_4, B64_5, B64_6, B64_7, B64_8, B64_9, plus, slash, padding
};


char getbase64character(const char in){
    if (in >= B64_A && in <= B64_Z) return 'A'+in-B64_A;
    if (in >= B64_a && in <= B64_z) return 'a'+in-B64_a;
    if (in >= B64_0 && in <= B64_9) return '0'+in-B64_0;
    if (in == plus) return '+';
    if (in == slash) return '/';
    if (in == padding) return '=';
    return '\0';
}

unsigned int base64encode(char * input, char ** poutput,unsigned int sizeofdata){
	unsigned int p = 0;
	unsigned int pad = 0;
    unsigned int lineLength = 0;
        
	growing_buffer output = {NULL,0,0};
	
	for (p = 0 ; p< sizeofdata ;  p += 3){
		appendChar(&output,getbase64character((input[p] & 0xFC) >> 2));
        lineLength+=1;
		if (p+1 < sizeofdata){
			appendChar(&output,(getbase64character(((input[p] & 0x03) <<4) | ((input[p+1] & 0xF0) >> 4))));
            lineLength+=1;
        }
		if(p+2 <sizeofdata) {
			appendChar(&output,(getbase64character(((input[p+1] & 0x0F) <<2) | ((input[p+2] & 0xC0) >>6))));
			appendChar(&output,(getbase64character((input[p+2] & 0x3F))));
            lineLength+=2;
		}
		
		if(p+1 == sizeofdata) {
			appendChar(&output,(getbase64character(((input[p] & 0x03) <<4))));
            lineLength+=1;
		}
		else if(p+2 == sizeofdata) {
			appendChar(&output,(getbase64character(((input[p+1] & 0x0F) <<2))));
            lineLength+=1;
		}
        if (lineLength>=76){
            appendString(&output,"\r\n");
            lineLength = 0;
        }
	}

    pad = (sizeofdata % 3);
    if(pad) {
        if(pad == 1){
            pad = 2;
        } else {
            pad = 1;
        }
    }
    for(p = 0; p < pad; ++p){
        appendChar(&output,'=');
    }
    appendChar(&output,'\0');
    *poutput = output.data;
    return output.current_length;
}



char * uuencode(FILE * file,int mode)
{
    #pragma unused(mode)
	int ch = 0;
    int n = 0;
	char *p = NULL;
	char buf[80];
    
    growing_buffer output = {NULL,0,0};
    
	while ((n = fread(buf, 1, 45, file))) {
		ch = ENC(n);
		if (fputc(ch, stdout) == EOF) break;
		for (p = buf; n > 0; n -= 3, p += 3) {
			/* Pad with nulls if not a multiple of 3. */
			if (n < 3) {
				p[2] = '\0';
				if (n < 2) p[1] = '\0';
			}
			ch = *p >> 2;
			ch = ENC(ch);
            appendChar(&output,ch);
			if (fputc(ch, stdout) == EOF) break;
            
			ch = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
			ch = ENC(ch);
            appendChar(&output,ch);
			if (fputc(ch, stdout) == EOF) break;
            
			ch = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
			ch = ENC(ch);
            appendChar(&output,ch);
			if (fputc(ch, stdout) == EOF) break;
            
			ch = p[2] & 077;
			ch = ENC(ch);
            appendChar(&output,ch);
			if (fputc(ch, stdout) == EOF) break;
		}
        appendChar(&output,'\n');
		if (fputc('\n', stdout) == EOF) break;
	}
    return output.data;
}

char * uuencodefile(char * pathToFile){
	int mode = 0;
    FILE * output = NULL;
    mode = RW & ~umask(RW);
	output = fopen(pathToFile,"r+");
	return uuencode(output,mode);
}

char * encode64file(char * pathToFile){
	growing_buffer input = {NULL,0,0};
	long count = 0;
	int finished = 0;
	int status = 0;
	char c[1025];
	FILE * fp = NULL;
	char * output = NULL;
	fp = fopen(pathToFile,"r");
    if (fp != NULL){
        while(!finished){
            status = fread(c,sizeof(char),1024,fp);
            if (status>0){
                c[status] = 0;
                appendBytesToString(&input,c,status);
                count += status;
            } else {
                output = NULL;
            }
            if (status == 0) finished = 1;
        }
        if(finished){
            base64encode(input.data, &output, input.current_length);
            fclose(fp);
            free(input.data);
        }
    }
    return output;
}
/* Special thanks to KDE, this algorithm is taken form KMail */

const char hexChars[16] ={'0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
const unsigned int maxQPLineLength = 80;

unsigned int quotedPrintableEncode(const char * data, char ** out, int useCRLF){
	unsigned int lineLength = 0;
	unsigned int i = 0;
	unsigned char c = '0';
    unsigned int length = 0;
    unsigned int end = 0;
	growing_buffer sout = {NULL,0,0};
    
    if (data != NULL){
        length = strlen(data);
        end = length - 1;
    } else {
        *out = NULL;
        return 0;
    }
	
	for (i = 0; i < length; i++){
		c = data[i];
		if ((c >= 33) && (c <= 126) && ('=' != c)) {
			appendChar(&sout,c);
			++lineLength;
		} else if (c == ' ') {
			if( (i >= length) || ( (i < end) && ( (useCRLF && ('\r' == data[i + 1]) && ('\n' == data[i + 2]) ) || ( !useCRLF && ('\n' == data[i + 1])) || (!useCRLF && ('\r' == data[i + 1]))))){
				appendString(&sout,"=20");
				lineLength += 3;
			} else {
				appendChar(&sout,c);
				lineLength += 1;
			}
		} else if ( (useCRLF && ('\r' == c) && (i < end) && ('\n' == data[i + 1])) || (!useCRLF && ('\n' == c)) || (!useCRLF && ('\r' == c)) ){
			lineLength = 0;
			appendString(&sout, "\r\n");
			if ( (i < end) && ('\n' == data[i + 1]) ) ++i;
		} else {
			appendChar(&sout, '=');
			appendChar(&sout,hexChars[c / 16]);
			appendChar(&sout,hexChars[c % 16]);
			lineLength += 3;
		}		
		if ((lineLength > maxQPLineLength) && (i < end)){
			appendString(&sout, "=\r\n");
			lineLength = 0;
		}
	}
	appendChar(&sout,'\0');
	*out = sout.data;
    return sout.current_length;
}